package com.tank.entrity;

import com.net.entity.TankJoinMessage;
import com.tank.control.DirectionEnum;
import com.tank.fire.DefaultFireStrategy;
import com.tank.fire.FireStrategy;
import com.tank.model.GameModel;
import com.tank.observer.TankFireEvent;
import com.tank.observer.TankFireHandle;
import com.tank.observer.TankFireObserver;

import java.awt.*;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Semaphore;

import static com.tank.window.GameApplicationWindows.WINDOWS_HEIGHT;
import static com.tank.window.GameApplicationWindows.WINDOWS_WIDTH;

public class Tank extends GameObject {
    /** 坦克的高度 */
    public final static int TANK_HEIGHT = 50;
    /** 坦克的宽度 */
    public final static int TANK_WIDTH = 50;
    /** 解决多线程数组越界 */
    public final static Semaphore BARREL = new Semaphore(1);
    /** 坦克的移动速度 */
    private final static double TANK_MOVE_SPEED = 0.2;

    /** 坦克分组类型 */
    private final GroupType groupType;
    /** 坦克方向的随机 */
    private Random directionRandom;
    /** 坦克随机开火 */
    private Random fireRandom;
    /** 碰撞检测使用的矩形 */
    private Rectangle tankRectangle = new Rectangle();

    /** 坦克方向 */
    private DirectionEnum directionEnum;
    /** 坦克是否在移动中 */
    private boolean moving;

    /** 记录上一次位置 */
    private double oldX, oldY;

    /** 坦克颜色 */
    private Color tankColor = Color.BLACK;
    /** fire的扩展 */
    private FireStrategy fireStrategy = new DefaultFireStrategy();
    /** 坦克是否存活 */
    private boolean activity = true;

    private UUID uuid;

    private List<TankFireObserver> fireObservers;

    public UUID getUuid() {
        return uuid;
    }

    public Tank(TankJoinMessage joinMessage) {
        this.x = joinMessage.x;
        this.y = joinMessage.y;
        this.directionEnum = joinMessage.dir;
        this.moving = joinMessage.moving;
        this.groupType = joinMessage.group;
        this.uuid = joinMessage.id;

        tankRectangle.x = (int)this.x;
        tankRectangle.y = (int)this.y;
        tankRectangle.width = TANK_WIDTH;
        tankRectangle.height = TANK_HEIGHT;
    }


    public Tank(int x, int y, DirectionEnum directionEnum, GroupType groupType) {
        this.x = x;
        this.y = y;
        this.directionEnum = directionEnum;
        this.groupType = groupType;
        this.directionRandom = new Random();
        this.fireRandom = new Random();
        this.fireObservers = Collections.singletonList(new TankFireHandle());
        this.uuid = UUID.randomUUID();
        this.tankRectangle = new Rectangle((int) this.x, (int) this.y, TANK_WIDTH, TANK_HEIGHT);
    }

    public Tank(int x, int y, DirectionEnum directionEnum, GroupType groupType, boolean moving) {
        this(x, y, directionEnum, groupType);
        this.moving = moving;
    }

    public Tank(int x, int y, DirectionEnum directionEnum, GroupType groupType, boolean moving, Color tankColor, UUID uuid) {
        this(x, y, directionEnum, groupType);
        this.tankColor = tankColor;
        this.uuid = uuid;
        this.moving = moving;
    }

    public Tank(int x, int y, DirectionEnum directionEnum, GroupType groupType, boolean moving, FireStrategy fireStrategy) {
        this(x, y, directionEnum, groupType, moving);
        if (fireStrategy != null) {
            this.fireStrategy = fireStrategy;
        }
    }

    public Tank(int x, int y, DirectionEnum directionEnum, GroupType groupType, boolean moving, FireStrategy fireStrategy, Color tankColor) {
        this(x, y, directionEnum, groupType, moving, fireStrategy);
        this.tankColor = tankColor;
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }
    public void setMoving(boolean moving) {
        this.moving = moving;
    }

    public boolean isMoving() {
        return this.moving;
    }

    public void back() {
        x = oldX;
        y = oldY;
    }

    public void paint(final Graphics graphics) {
        try {
            if (!activity) {
                GameModel.getInstance().removeObject(this);
                return;
            }
            graphics.drawString(String.valueOf(this.uuid), (int) (this.x - String.valueOf(this.getUuid()).length()), (int) (this.y - 5));
            graphics.setColor(tankColor);
            graphics.fillRect((int) x, (int) y, TANK_WIDTH, TANK_HEIGHT);
            move();
        } catch (Exception e) {
            e.getLocalizedMessage();
        }
    }

    @Override
    public int getWidth() {
        return TANK_WIDTH;
    }

    @Override
    public int getHeight() {
        return TANK_HEIGHT;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public GroupType getGroupType() {
        return groupType;
    }

    public DirectionEnum getDirectionEnum() {
        return directionEnum;
    }

    public void move() {
        oldX = x;
        oldY = y;
        if (!moving) {
            return;
        }
        switch (directionEnum) {
            case LEFT:
                if (x > 0)
                x -= TANK_MOVE_SPEED;
                break;
            case RIGHT:
                if (x < WINDOWS_WIDTH - TANK_WIDTH)
                x += TANK_MOVE_SPEED;
                break;
            case UP:
                if (y > 30)
                y -= TANK_MOVE_SPEED;
                break;
            case DOWN:
                if (y < WINDOWS_HEIGHT - TANK_HEIGHT)
                y += TANK_MOVE_SPEED;
                break;
        }
        this.tankRectangle.x = (int) this.x;
        this.tankRectangle.y = (int) this.y;
        fireRandom();
        directionRandom();
    }

    public void fireRandom() {
        if (this.fireRandom.nextInt(10000) > 9990 & this.groupType == GroupType.BAD) {
            this.fire();
        }
    }

    public void handleFireKey() {
        fireObservers.forEach(item -> item.actionOnFire(new TankFireEvent(this)));
    }

    public void directionRandom() {
        if (this.groupType == GroupType.BAD & this.fireRandom.nextInt(10000) > 9990) {
            this.directionEnum = DirectionEnum.values()[this.directionRandom.nextInt(4)];
        }
    }

    public void setDirectionEnum(DirectionEnum directionEnum) {
        this.directionEnum = directionEnum;
    }

    public void fire() {
        fireStrategy.fire(this);
    }

    public Rectangle getTankRectangle() {
        return tankRectangle;
    }

    public void destroy() {
        this.activity = false;
    }
}
